package com.zcm.modules.cxf.utils;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * RSA加密，字符串格式的公钥、密钥、加密后的密文均为Base64编码
 * 
 * @author zhangdp
 * @date 2018-05-30
 */
public class RSAUtils {
	final static Base64 base64 = new Base64();
	/**
	 * Cipher转换名称（java RSA=RSA/ECB/PKCS1Padding）
	 */
	private static final String TRANSFORMATION_NAME = "RSA";
	/**
	 * 加密算法RSA
	 */
	private static final String KEY_ALGORITHM = "RSA";
	/**
	 * RSA加密byte大小
	 */
	private static final int MAX_ENCRYPT_BLOCK = 117;

	/**
	 * RSA解密byte大小
	 */
	private static final int MAX_DECRYPT_BLOCK = 128;

	/**
	 * 生成密钥对
	 * 
	 * @param
	 * @return 2个字符串元素的数组，第1个元素为Base64编码的公钥，第2个元素为Base64编码的私钥
	 * @throws Exception
	 */
	public static String[] genKeyPair() throws Exception {
		String[] keys = new String[2];
		KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
		keyPairGenerator.initialize(1024);
		KeyPair keyPair = keyPairGenerator.generateKeyPair();
		PublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		PrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
		keys[0] = base64.encodeToString(publicKey.getEncoded());
		keys[1] = base64.encodeToString(privateKey.getEncoded());
		return keys;
	}

	/**
	 * 将base64编码后的公钥字符串转成PublicKey实例
	 * 
	 * @param publicKey
	 * @return
	 * @throws Exception
	 */
	public static PublicKey getPublicKey(String publicKey) throws Exception {
		byte[] keyBytes = base64.decode(publicKey);
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(TRANSFORMATION_NAME);
		return keyFactory.generatePublic(keySpec);
	}

	/**
	 * 将base64编码后的私钥字符串转成PrivateKey实例
	 * 
	 * @param privateKey
	 * @return
	 * @throws Exception
	 */
	public static PrivateKey getPrivateKey(String privateKey) throws Exception {
		byte[] keyBytes = base64.decode(privateKey);
		PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(TRANSFORMATION_NAME);
		return keyFactory.generatePrivate(keySpec);
	}

	public static byte[] encrypt(byte[] content, PublicKey publicKey) throws Exception {
		Cipher cipher = Cipher.getInstance(TRANSFORMATION_NAME);
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);
		int inputLen = content.length;
		int offSet = 0;
		byte[] cache;
		int i = 0;
		byte[] ret = getBytes(content, cipher, offSet, i, inputLen, MAX_ENCRYPT_BLOCK);
		return ret;
	}

	public static String encryptToStr(byte[] content, PublicKey publicKey) throws Exception {
		return base64.encodeToString(encrypt(content, publicKey));
	}

	public static byte[] encrypt(String content, PublicKey publicKey) throws Exception {
		return encrypt(content.getBytes(), publicKey);
	}

	public static String encryptToStr(String content, PublicKey publicKey) throws Exception {
		return encryptToStr(content.getBytes(), publicKey);
	}

	public static byte[] encrypt(byte[] content, String publicKey) throws Exception {
		return encrypt(content, getPublicKey(publicKey));
	}

	public static String encryptToStr(byte[] content, String publicKey) throws Exception {
		return encryptToStr(content, getPublicKey(publicKey));
	}

	public static byte[] encrypt(String content, String publicKey) throws Exception {
		return encrypt(content, getPublicKey(publicKey));
	}

	public static String encryptToStr(String content, String publicKey) throws Exception {
		return encryptToStr(content, getPublicKey(publicKey));
	}

	/**
	 * 私钥解密
	 * 
	 * @param content
	 * @param privateKey
	 * @return
	 * @throws Exception
	 */
	public static byte[] decrypt(byte[] content, PrivateKey privateKey) throws Exception {
		Cipher cipher = Cipher.getInstance(TRANSFORMATION_NAME);
		cipher.init(Cipher.DECRYPT_MODE, privateKey);
		int offSet = 0;
		int i = 0;
		int len = content.length;
		byte[] cache;
		byte[] ret;
		// 对数据分段解密
		ret = getBytes(content, cipher, offSet, i, len, MAX_DECRYPT_BLOCK);
		return ret;
	}

	private static byte[] getBytes(byte[] content, Cipher cipher, int offSet, int i, int len, int maxDecryptBlock) throws Exception {
		byte[] cache;
		byte[] ret;
		try {
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			while (len - offSet > 0) {
				if (len - offSet > maxDecryptBlock) {
					cache = cipher.doFinal(content, offSet, maxDecryptBlock);
				} else {
					cache = cipher.doFinal(content, offSet, len - offSet);
				}
				out.write(cache, 0, cache.length);
				i++;
				offSet = i * maxDecryptBlock;
			}
			ret = out.toByteArray();
		} catch (Exception e) {
			throw e;
		}
		return ret;
	}

	public static String decryptToStr(byte[] content, PrivateKey privateKey) throws Exception {
		return new String(decrypt(content, privateKey));
	}

	public static byte[] decrypt(String content, PrivateKey privateKey) throws Exception {
		return decrypt(base64.decode(content), privateKey);
	}

	public static String decryptToStr(String content, PrivateKey privateKey) throws Exception {
		return new String(decrypt(content, privateKey));
	}

	public static byte[] decrypt(byte[] content, String privateKey) throws Exception {
		return decrypt(content, getPrivateKey(privateKey));
	}

	public static String decryptToStr(byte[] content, String privateKey) throws Exception {
		return new String(decrypt(content, getPrivateKey(privateKey)));
	}

	public static byte[] decrypt(String content, String privateKey) throws Exception {
		return decrypt(content, getPrivateKey(privateKey));
	}

	public static String decryptToStr(String content, String privateKey) throws Exception {
		return new String(decrypt(content, getPrivateKey(privateKey)));
	}

	public static void main(String[] args) {
		String str = "666666";
		try {
			String[] keys = RSAUtils.genKeyPair();
			System.out.println("生成公钥：" + keys[0]);
			System.out.println("生成私钥：" + keys[1]);
			System.out.println("明文：" + str);
			String str1 = RSAUtils.encryptToStr(str, keys[0]);
			System.out.println("公钥加密：" + str1);
			String str2 = RSAUtils.decryptToStr(str1, keys[1]);
			System.out.println("私钥解密解密：" + str2);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
